package com.dnb.webmash.playmaker.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

import com.dnb.webmash.playmaker.client.mediaplayer.MediaPlayer;
import com.dnb.webmash.playmaker.client.mediaplayer.MediaPlayersManager;
import com.dnb.webmash.playmaker.shared.LoginInfo;
import com.dnb.webmash.playmaker.shared.MediaType;
import com.dnb.webmash.playmaker.shared.MyURL;
import com.dnb.webmash.playmaker.shared.Option;
import com.dnb.webmash.playmaker.shared.PlayerConfig;
import com.dnb.webmash.playmaker.shared.PlayerType;
import com.dnb.webmash.playmaker.shared.PlaylistItem;
import com.dnb.webmash.playmaker.shared.Utility;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HorizontalSplitPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.user.client.ui.RichTextArea;

@SuppressWarnings("deprecation")
public class PlayMaker implements EntryPoint {
	private static final AppConstants CONSTANTS = GWT.create(AppConstants.class);	
	private final WebPlaylistServiceAsync webPlaylistService = GWT.create(WebPlaylistService.class);
	private final LinkedHashMap<TreeItem, PlaylistItem> playlistItems = new LinkedHashMap<TreeItem, PlaylistItem>(); 
	private final LinkedHashMap<UniqueTreeItem, PlaylistItem> playableItems = new LinkedHashMap<UniqueTreeItem, PlaylistItem>();
	private final LinkedHashMap<TreeItem, TreeItem> nowPlayingItems = new LinkedHashMap<TreeItem, TreeItem>();
	private final LinkedHashMap<MediaType, Boolean> typesFilter = new LinkedHashMap<MediaType, Boolean>(); //TODO IMPLEMENT FILTERING THE TREE WITH THIS
	private final LinkedHashMap<MyURL, TreeItem> requestedURLS = new LinkedHashMap<MyURL, TreeItem>();
	private int index = -1, rereqIndex = 0; 
	private static final int comboboxMax = 9, internalReqDepthMax = 1; //first one is the lazy loading depth, second is fixed internal recursion depth.
	private MediaPlayersManager mediaPlayersManager;
	private final Tree tree = new Tree();
	private final Tree tree_1 = new Tree();
	private final TreeItem nowPlaying = new TreeItem(CONSTANTS.nowPlaying_html());
	private final Label errorLabel = new Label();	
	private final Label errorLabel_2 = new Label();
	private final Label errorLabel_1 = new Label();
	private final Label itemInfoLbl = new Label();
	private final CellTable<Option> configsCellTable = new CellTable<Option>();
	final Button getChanButton = new Button(CONSTANTS.getChanButton_html());
	private ListBox comboBox = new ListBox();
	private VerticalPanel mediatypesVerticalPanel = new VerticalPanel();
	private LoginInfo loginInfo = null;
    private Anchor signInLink = new Anchor("Sign in");
    private Anchor signOutLink = new Anchor("Sign out");	
    private boolean isLoggedIn = false;
	protected Boolean requestMediaOnly = true, internalZeroRec = true; //default to only media and quick request
	private String requestID;
	
	public void onModuleLoad() {
		/*
		 * UI AND CONTROLLER
		 * WATCH TAB
		 */
		final DecoratedTabPanel mainDecoratedTabPanel = new DecoratedTabPanel();
		RootPanel.get().add(mainDecoratedTabPanel);
		mainDecoratedTabPanel.setWidth(CONSTANTS.mainDecoratedTabPanel_width());
		final int windowHeight = Window.getClientHeight();
		mainDecoratedTabPanel.setHeight(windowHeight + "px");

		final DockLayoutPanel tab0_watchDockLayoutPanel = new DockLayoutPanel(Unit.EM);
		mainDecoratedTabPanel.add(tab0_watchDockLayoutPanel, CONSTANTS.tab0_watchDockLayoutPanel_TabText(), false);
		tab0_watchDockLayoutPanel.setSize("100%", windowHeight + "px");
		mainDecoratedTabPanel.selectTab(0);// Select the player tab

		// Create a handler for the nextButton
		class NextButtonHandler implements ClickHandler, KeyUpHandler {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
					PlayMaker.this.playPrevNextMedia(1);
			}

			@Override
			public void onClick(ClickEvent event) {
				PlayMaker.this.playPrevNextMedia(1);
			}
		}
		// Create a handler for the nextButton
		class PrevButtonHandler implements ClickHandler, KeyUpHandler {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
					PlayMaker.this.playPrevNextMedia(-1);
			}

			@Override
			public void onClick(ClickEvent event) {
				PlayMaker.this.playPrevNextMedia(-1);
			}
		}

		final NextButtonHandler handlNext = new NextButtonHandler();
		final PrevButtonHandler handlPrev = new PrevButtonHandler();

		final HorizontalPanel inputControlsPanel = new HorizontalPanel();
		tab0_watchDockLayoutPanel.addNorth(inputControlsPanel, 2.0);

		final TextBox textBox = new TextBox();
		textBox.setTitle(CONSTANTS.textBox_title());
		inputControlsPanel.add(textBox);
		textBox.setWidth(CONSTANTS.textBox_width());//TODO FIX NOT SHOWING UP
		textBox.setFocus(true);
		textBox.selectAll();
							
		for (int i=0; i < comboboxMax; i++) comboBox.addItem(i+"");
		comboBox.setTitle(CONSTANTS.comboBox_title());
		inputControlsPanel.add(comboBox);
		
		CheckBox requestOnlyMedia_checkBox = new CheckBox();
		requestOnlyMedia_checkBox.setTitle(CONSTANTS.checkBox_title());
		requestOnlyMedia_checkBox.setValue(this.requestMediaOnly);
		inputControlsPanel.add(requestOnlyMedia_checkBox);
		requestOnlyMedia_checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				PlayMaker.this.requestMediaOnly = event.getValue();
			}
		});
		
		CheckBox enableInternalRec = new CheckBox();
		enableInternalRec.setTitle(CONSTANTS.simpleCheckBox_title_1());
		enableInternalRec.setValue(this.internalZeroRec);
		inputControlsPanel.add(enableInternalRec);
		enableInternalRec.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				PlayMaker.this.internalZeroRec = event.getValue();
			}
		});
		
		getChanButton.setTitle(CONSTANTS.getChanButton_title());
		inputControlsPanel.add(getChanButton);
		getChanButton.setText(CONSTANTS.getChanButton_text());
		getChanButton.addStyleName("getChannelButton");

		Button btnPrev = new Button(CONSTANTS.btnPrev_html());
		btnPrev.setTitle(CONSTANTS.btnPrev_title());
		inputControlsPanel.add(btnPrev);
		// getChanButton.setWidth(60+"px");

		final Button btnNext = new Button(CONSTANTS.btnNext_html());
		btnNext.setTitle(CONSTANTS.btnNext_title());
		inputControlsPanel.add(btnNext);
		
		Button btnSave = new Button(CONSTANTS.btnSave_html());
		btnSave.addKeyUpHandler(new KeyUpHandler() {
			public void onKeyUp(KeyUpEvent event) {
				if (PlayMaker.this.isLoggedIn){
					if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER){
						TreeItem sel = tree.getSelectedItem();
						//Verify this was one of the requested URLS and not a system-owned node (cant save those)
						if (playableItems.containsKey(sel)||requestedURLS.containsValue(sel)){
							PlaylistItem plsit = PlayMaker.this.getPlaylistItemFromTreeItem(sel);
				        	PlayMaker.this.requestSavePlsToServer(plsit);
			        	}
						else errorLabel.setText("Can only save requested playlists or media items");
					}
				} else errorLabel.setText("Not logged in - cannot save.");
			}
		});
		btnSave.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				if (PlayMaker.this.isLoggedIn){
					TreeItem sel = tree.getSelectedItem();
					//Verify this was one of the requested URLS and not a system-owned node (cant save those)
					if (playableItems.containsKey(sel)||requestedURLS.containsValue(sel)){
						PlaylistItem plsit = PlayMaker.this.getPlaylistItemFromTreeItem(sel);
			        	PlayMaker.this.requestSavePlsToServer(plsit);
		        	}
					else errorLabel.setText("Can only save requested playlists or media items");
				} else errorLabel.setText("Not logged in - cannot save.");
			}
		});
		btnSave.setTitle("Saves the selected playlist so that you can continue watching next time you log back in - Warning, overrides previous saved sessions.");
		inputControlsPanel.add(btnSave);
		
		Button btnLoad = new Button(CONSTANTS.btnLoad_html());
		btnLoad.setTitle(CONSTANTS.btnLoad_title());
		btnLoad.addKeyUpHandler(new KeyUpHandler() {
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER){
					PlayMaker.this.requestLoadPlsFromServer();
				}
			}
		});
		btnLoad.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
	        	PlayMaker.this.requestLoadPlsFromServer();
			}
		});
		inputControlsPanel.add(btnLoad);

		// Set up login link for signin
		// Check login status using login service.
	    LoginServiceAsync loginService = GWT.create(LoginService.class);
	    loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
	      public void onFailure(Throwable error) {
	    	  errorLabel.setText("Unable to verify login status.");
	      }	      
	      public void onSuccess(LoginInfo result) {
	        loginInfo = result;
	        if(loginInfo.isLoggedIn()) {
	        	signOutLink.setHref(loginInfo.getLogoutUrl());
	        	inputControlsPanel.add(signOutLink);
	        	PlayMaker.this.isLoggedIn = true;
	        	//Auto load the current users last session
	        	PlayMaker.this.requestLoadPlsFromServer();
	        	
	        	//only show these items if the logged in user is admin
	        	if (loginInfo.isAdmin()){
	        		itemInfoLbl.setVisible(true);
	        		//Load the global playlist for admins
		        	PlayMaker.this.requestLoadGlobalPlsFromServer();
	        	}
	        	else
	        		itemInfoLbl.setVisible(false);
	        } else {
	        	signInLink.setHref(loginInfo.getLoginUrl());
	        	inputControlsPanel.add(signInLink);	
	        	PlayMaker.this.isLoggedIn = false;
	        	//hide admin only things if not logged in
	        	itemInfoLbl.setVisible(false);
	        }
	      }
	    });
	    
		// Create a handler for the sendButton and nameField
		class GetChanButtonHandler implements ClickHandler, KeyUpHandler {
			/**
			 * Fired when the user clicks on the sendButton.
			 */
			public void onClick(ClickEvent event) {
				this.sendURLToServer();
			}

			/**
			 * Fired when the user types in the nameField.
			 */
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
					this.sendURLToServer();
			}

			/**
			 * Send the name from the nameField to the server and wait for a
			 * response.
			 */
			private void sendURLToServer() {
				final String textToServer = textBox.getText();
				PlayMaker.this.requestPlaylistFromServer(textToServer);
			}
		}

		// HANDLERS::
		// Add a handler to send the name to the server
		final GetChanButtonHandler handler = new GetChanButtonHandler();

		btnNext.addClickHandler(handlNext);
		btnNext.addKeyUpHandler(handlNext);
		btnPrev.addClickHandler(handlPrev);
		btnPrev.addKeyUpHandler(handlPrev);
		getChanButton.addClickHandler(handler);
		textBox.addKeyUpHandler(handler);

		HorizontalSplitPanel playerPlaylistSplitPanel = new HorizontalSplitPanel();
		tab0_watchDockLayoutPanel.add(playerPlaylistSplitPanel);

		// Add the textbox and treenode viewer
		final VerticalPanel playlistPanel = new VerticalPanel();
		playerPlaylistSplitPanel.setLeftWidget(playlistPanel);
		playlistPanel.setSize("100%", "100%");
		playlistPanel.add(this.errorLabel);
		
		// ADDING THE TREE
		this.nowPlaying.setVisible(false);
		tree.setAnimationEnabled(false);
		playlistPanel.add(tree);
		playlistPanel.setCellHeight(tree, "100%");
		playlistPanel.setCellWidth(tree, "100%");
		tree.setSize("100%", "70%");
		this.tree.addItem(this.nowPlaying);		
		itemInfoLbl.setVisible(false);
		playlistPanel.add(itemInfoLbl);
		
		/*TREE SCROLL FIX
		DOM.setStyleAttribute(this.tree.getElement(), "position", "static");
		this.tree.addFocusHandler(new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				return;
			}
		});
		*/// END TREE SCROLL FIX
		
		this.tree.addSelectionHandler(new SelectionHandler<TreeItem>() {
			public void onSelection(SelectionEvent<TreeItem> event) {
				final TreeItem sel = event.getSelectedItem();								
				PlaylistItem plsit = getPlaylistItemFromTreeItem(sel);				
				if (plsit!=null){								
					PlayMaker.this.itemInfoLbl.setText(plsit.toPrettyString()); //TODO MAYBE ADD A COMMENTS SECTION THAT THE USER CAN EDIT NEAR THIS UI
					if (plsit.isPlayable())
						PlayMaker.this.embedPlsItem(plsit);
				}
				else
					PlayMaker.this.itemInfoLbl.setText("No PlaylistItem linked to selected TreeItem!");
			}
		});
		this.tree.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				String url;
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					final TreeItem sel = PlayMaker.this.tree.getSelectedItem();
					PlaylistItem plsit = PlayMaker.this.getPlaylistItemFromTreeItem(sel);
					if (!plsit.isPlayable()) {						
						url = sel.getText();
						if (ClientFieldVerifier.isValidURL(url)) {
							textBox.setText(url);
							getChanButton.click();
						}else {
							if (ClientFieldVerifier.isValidURL(plsit.getFullURL())) {
								textBox.setText(plsit.getFullURL());
								getChanButton.click();
							}
						}
					} else
						PlayMaker.this.embedPlsItem(plsit);					
				}
			}
		});

		// The universal media player
		this.mediaPlayersManager = new MediaPlayersManager();
		playerPlaylistSplitPanel.setRightWidget(mediaPlayersManager);
		mediaPlayersManager.setSize("100%", "100%");

		/****************************************************************************************************************
		 * END OF WATCH TAB
		 * START OF MEDIATYPES TAB
		 ****************************************************************************************************************/										
		final DockLayoutPanel tab1_mediaTypesDockLayoutPanel = new DockLayoutPanel(Unit.EM);
		mainDecoratedTabPanel.add(tab1_mediaTypesDockLayoutPanel, CONSTANTS.tab2_mediaTypesDockLayoutPanel_TabText(), false);
		tab1_mediaTypesDockLayoutPanel.setSize("100%", windowHeight + "px");		
		
		HorizontalPanel horizontalPanel_1 = new HorizontalPanel();
		tab1_mediaTypesDockLayoutPanel.addNorth(horizontalPanel_1, 2.0);

		Button resetButton = new Button(CONSTANTS.resetButton_html());
		resetButton.setTitle(CONSTANTS.resetButton_title());
		resetButton.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					mediatypesVerticalPanel.clear();
					PlayMaker.this.requestMediatypesListFromServer();
				}					
			}
		});
		resetButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				mediatypesVerticalPanel.clear();
				PlayMaker.this.requestMediatypesListFromServer();
			}
		});
		resetButton.setText(CONSTANTS.resetButton_text());
		horizontalPanel_1.add(resetButton);
		
		Button btnUpdate = new Button(CONSTANTS.btnUpdate_html());
		btnUpdate.setTitle(CONSTANTS.btnUpdate_title());
		btnUpdate.addKeyUpHandler(new KeyUpHandler() {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				PlayMaker.this.requestLoadMediaDB();
			}
			
		});
		btnUpdate.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				PlayMaker.this.requestLoadMediaDB();
			}
			
		});
		
		horizontalPanel_1.add(btnUpdate);
		horizontalPanel_1.add(errorLabel_1);
		ScrollPanel scrollPanel = new ScrollPanel();
		tab1_mediaTypesDockLayoutPanel.addWest(scrollPanel, 9.5);
		scrollPanel.setSize("100%", "100%");
		scrollPanel.setWidget(mediatypesVerticalPanel);
		mediatypesVerticalPanel.setSize("100%", "100%");
		
		tab1_mediaTypesDockLayoutPanel.add(tree_1);
		tree_1.setSize("100%", "100%");
		
		/* END OF MEDIATYPES TAB
		 * START OF ADD MEDIA TAB
		 */
		
		DockLayoutPanel tab5_addmediapanel = new DockLayoutPanel(Unit.EM);
		mainDecoratedTabPanel.add(tab5_addmediapanel, CONSTANTS.tab5_addmediapanel_TabText(), false);
		tab5_addmediapanel.setSize("100%", windowHeight + "px");
		
		//THE TEST PLAYER INIT... (added near the end of this tab)
		final MediaPlayersManager testPlayer = new MediaPlayersManager();
		testPlayer.setSize("100%", "100%");

		final RichTextArea embedCodeTextArea = new RichTextArea();
		embedCodeTextArea.setStyleName("gwt-EmbedCodeRichTextArea");
		embedCodeTextArea.setSize("75%", "100%");
		
		HorizontalPanel horizontalPanel = new HorizontalPanel();
		tab5_addmediapanel.addNorth(horizontalPanel, 2.0);
		
		final Button button = new Button("Test");
		horizontalPanel.add(button);
		
		button.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				//grab the text from the embed-box
				String txt = embedCodeTextArea.getText();
				//TODO verify it isnt malicious
				//then create media item
				PlaylistItem f = new PlaylistItem("PlaylistItemToTest");
				f.setElement(txt);					  
				f.setPlayerType(PlayerType.EMBED);
				f.setPlayable(true);
				//and load it on the test player
				testPlayer.embedItem(f);
			}
		});
		
		Button button_1 = new Button("Save As...");
		horizontalPanel.add(button_1);
		
		button_1.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				final MyPopup popup = new MyPopup();
				popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
			          public void setPosition(int offsetWidth, int offsetHeight) {
			            int left = (Window.getClientWidth() - offsetWidth) / 3;
			            int top = (Window.getClientHeight() - offsetHeight) / 3;
			            popup.setPopupPosition(left, top);
			          }
			        });
			}
		});
		
		VerticalPanel addMediaVerticalPanCentre = new VerticalPanel();
		tab5_addmediapanel.add(addMediaVerticalPanCentre);
		addMediaVerticalPanCentre.setSize("100%", "100%");
		addMediaVerticalPanCentre.add(embedCodeTextArea);
		addMediaVerticalPanCentre.add(testPlayer);
		
		/* END OF ADD MEDIA TAB
		 * START OF MYPLAYER TAB
		 */
		final DockLayoutPanel tab2_playerConfigDockLayoutPanel = new DockLayoutPanel(Unit.EM);
		mainDecoratedTabPanel.add(tab2_playerConfigDockLayoutPanel, CONSTANTS.tab1_playerConfigDockLayoutPanel_TabText(), false);
		tab2_playerConfigDockLayoutPanel.setSize("100%", windowHeight + "px");
		
		HorizontalPanel horizontalPanel_2 = new HorizontalPanel();
		tab2_playerConfigDockLayoutPanel.addNorth(horizontalPanel_2, 2.0);
		
		final ListBox comboBox_2 = new ListBox(false);
		comboBox_2.setTitle(CONSTANTS.comboBox_2_title());
		horizontalPanel_2.add(comboBox_2);		
		
		CheckBox chckbxApplySettings = new CheckBox(CONSTANTS.chckbxApplySettings_text());
		chckbxApplySettings.setTitle(CONSTANTS.chckbxApplySettings_title());
		chckbxApplySettings.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				PlayMaker.this.mediaPlayersManager.enableSettings(event.getValue());
			}
		});
		horizontalPanel_2.add(chckbxApplySettings);
		//ADDING THIS LAST
		horizontalPanel_2.add(errorLabel_2);//TODO FIX THIS UI LOOKS BAD
		
		configsCellTable.setSize("100%", "100%");
		// Create name column.
	    TextColumn<Option> nameColumn = new TextColumn<Option>() {
	      @Override
	      public String getValue(Option op) {
	        return op.getMyName();
	      }
	    };
	    //create values column.
	    TextColumn<Option> valuesCol = new TextColumn<Option>() {
		      @Override
		      public String getValue(Option op) {
		    	  String result="";
		    	  ArrayList<String> optVals = op.getMyOptions();
		        for (int i =0; i < optVals.size(); i++){
		        	result +="("+i+") "+ optVals.get(i);
		        	if (i !=optVals.size()-1)
		        		result +=", ";
		        }
		        return result;
		      }
		    };
	    //create selected index colum.
		    TextColumn<Option> selVal = new TextColumn<Option>() {
			      @Override
			      public String getValue(Option op) {
			        return Integer.toString(op.getSetOption());
			      }
			    };

	    configsCellTable.addColumn(nameColumn, "Name");
	    configsCellTable.addColumn(valuesCol, "Possible Values");
	    configsCellTable.addColumn(selVal, "Selected Value");

		tab2_playerConfigDockLayoutPanel.add(configsCellTable);
		comboBox_2.addItem("");
		
		for (PlayerType t : PlayerType.values()){
			comboBox_2.addItem(t.name());
		}
		
		//Selecting a playertype in the combobox to see the user saved prefs or the player type defaults
		comboBox_2.addChangeHandler(new ChangeHandler(){
			@Override
			public void onChange(ChangeEvent event) {
				// TODO Auto-generated method stub
				int c = comboBox_2.getSelectedIndex()-1;//TODO FIGURE OUT WHY THE COMBOBOX RETURNS WRONG already working?
				errorLabel_2.setText("SELECTED :"+PlayerType.get(c));//TODO TESTING
				PlayMaker.this.requestLoadPlayerPrefsFromServer(PlayerType.get(c));
			}
			
		});
		/*
		 * END OF MYPLAYER TAB
		 * START OF IFRAME ABOUT AND CONFIG TABS
		 */
		
		//STATIC iFrame TABS CONTAINING ABOUT AND CONTACT PAGES
		final Frame tab3_aboutFrame = new Frame("About");
		tab3_aboutFrame.setUrl(CONSTANTS.tab3_aboutFrame_url());
		mainDecoratedTabPanel.add(tab3_aboutFrame, CONSTANTS.tab3_aboutFrame_TabText(), false);
		tab3_aboutFrame.setSize("100%", windowHeight + "px");
		
		final Frame tab4_contactFrame = new Frame("Contact");
		tab4_contactFrame.setUrl(CONSTANTS.tab4_contactFrame_url());
		mainDecoratedTabPanel.add(tab4_contactFrame, CONSTANTS.tab4_contactFrame_TabText(), false);
		tab4_contactFrame.setSize("100%", windowHeight + "px");			
		
		//**************************************** END OF TABS ***************************************************
		// Track changes to window size
		Window.addResizeHandler(new ResizeHandler() {
			public void onResize(ResizeEvent event) {
				final int height = event.getHeight();
				mainDecoratedTabPanel.setHeight(height + "px");
				tab0_watchDockLayoutPanel.setSize("100%", height + "px");
				tab2_playerConfigDockLayoutPanel.setSize("100%", height + "px");
				tab1_mediaTypesDockLayoutPanel.setSize("100%", height + "px");
				tab3_aboutFrame.setSize("100%", height + "px");
				tab4_contactFrame.setSize("100%", height + "px");				
				//TODO add all tabs here
			}
		});	
		//SELECTION HANDLER FOR THE MAIN TABS
		mainDecoratedTabPanel.addSelectionHandler(new SelectionHandler<Integer>(){//make a request to load the mediatypes list when the tab is selected
			@Override
			public void onSelection(SelectionEvent<Integer> event) {//WHICH TAB IS SELECTED?
				//TODO add if tab = 0 (playertab) then load video or do something
				//TODO TURN THIS INTO A SWITCH STATEMENT
				if (event.getSelectedItem().equals(Integer.valueOf(1))) PlayMaker.this.requestMediatypesListFromServer();							
				else if (event.getSelectedItem().equals(Integer.valueOf(2))){
					MediaPlayer curPlay = mediaPlayersManager.getCurrentPlayer();
					if (curPlay !=null){
						int t = curPlay.getType().getValue();						
						comboBox_2.setItemSelected(t, true);
					 
						if (mediaPlayersManager.hasPrefs()){
							errorLabel_2.setText("Current Preferences for "+curPlay.getType().toString());
							ArrayList<Option> currOpt = mediaPlayersManager.getCurrentOptions();
							PlayMaker.this.displayOpts(currOpt);							
							}
						else
						{
							configsCellTable.setRowCount(0,true);							
							PlayMaker.this.requestDefaultPrefsFromServer(curPlay.getType());//this calls displayopts only onsuccess													
						}
					}
					else errorLabel_2.setText("Select player from dropdown list");
				}
			}
		});
	}
	/****************************************************************************************************************
	 * END OF ON MODULE LOAD METHOD
	 * Start of Helpers
	 ****************************************************************************************************************/
	/*
	 * Adds playlist to tree view and to the master playableItems list
	 * @returns the last TreeItem added to the tree
	 */
	private Boolean addPlaylist(ArrayList<PlaylistItem> vals, Tree tree, Label myLabel) {				
		TreeItem parent = null;
		Boolean addedSomething = false;
		for (int i = 0; i < vals.size(); i++) {//each one should be a playlist: a URL root with media and links within
			final PlaylistItem it = vals.get(i);
			if (requestedURLS.containsKey(it.getURL())){tree.removeItem(requestedURLS.get(it.getURL()));//remove the nodes about to be updated
			}
			parent = new TreeItem();
			parent.setText(it.getName());
			tree.addItem(parent);//add it
			this.playlistItems.put(parent,it);
			if (it.getURL()!=null) {this.requestedURLS.put(it.getURL(),parent);	//Remember the added playlists								
			}
			if (it.hasChildren()) { //did we find anything?
				addedSomething = true;
				this.addPlaylistNode(it.getChildren(), parent);
			} else {//empty								
				tree.removeItem(parent);
				if (myLabel!=null) myLabel.setText("No media in playlist: "+ it.getName());
			}
		}
		return addedSomething; //returns the last element added
	}

	// recursive helper method
	private void addPlaylistNode(LinkedHashSet<PlaylistItem> vals, TreeItem branch) {		//TODO HERE ADD CODE TO STORE A mini NOW PLAYING IN EACH REQUESTED NODE, WITH THEIR MEDIA ONLY
		Iterator<PlaylistItem> iter = vals.iterator();
		while (iter.hasNext()){
			PlaylistItem it = iter.next();
			if (it.hasChildren()) {
				TreeItem parent = new TreeItem();
				parent.setText(it.getName());
				branch.addItem(parent);
				this.playlistItems.put(parent, it);
				this.addPlaylistNode(it.getChildren(), parent);
			} else {
				final UniqueTreeItem ti = new UniqueTreeItem(it.getName());
				branch.addItem(ti);
				this.playlistItems.put(ti, it);
				if (it.isPlayable()) this.playableItems.put(ti, it);// has no children so could be playable item
			}
		}
	}
	
	private PlaylistItem getPlaylistItemFromTreeItem(TreeItem sel){
		PlaylistItem item = PlayMaker.this.playlistItems.get(sel);
		if (item==null) {//might be in now playing
			final TreeItem nowPlaying = PlayMaker.this.nowPlayingItems.get(sel);
			item = PlayMaker.this.playableItems.get(nowPlaying);			
			// TODO add highlighting to now playing node using font color or css (leave tree.setSelected)
		}
		return item;
	}
	
	private boolean embedPlsItem(PlaylistItem sel) {
		if ((sel!=null) && (sel.isPlayable())){								
			return this.mediaPlayersManager.embedItem(sel);
		} else
			this.errorLabel.setText("Error embedding media!");
		return false;
	}

	//updates the now playing node and also stores any new mediaitem to the datastore
	private void updateAllMediaNode() {
		this.nowPlaying.removeItems();// clear
		this.nowPlayingItems.clear();
		final ArrayList<UniqueTreeItem> keys = new ArrayList<UniqueTreeItem>(this.playableItems.keySet());
		for (int i = 0; i < keys.size(); i++) {
			TreeItem it = new TreeItem();
			it.setText(keys.get(i).getText());
			this.nowPlayingItems.put(it, keys.get(i));
			this.nowPlaying.addItem(it);
		}
		if (!keys.isEmpty()){
			this.nowPlaying.setVisible(true);
			if (mediaPlayersManager.getCurrentPlayer()==null){
				playPrevNextMedia(1);// load next
			}
		}
		else this.nowPlaying.setVisible(false);
	}

	// Cycles the index counter for the playableItems counter
	private void playPrevNextMedia(int dir) {
		if (dir >0)
		{	
			if (this.index+1 < this.playableItems.size())
				this.index++;
			else
				this.index = 0;
					
		}
		else
		{
			if (this.index-1 > -1)
				this.index--;
			else
				this.index = this.playableItems.size()-1;
		}
		final ArrayList<UniqueTreeItem> keys = new ArrayList<UniqueTreeItem>(this.playableItems.keySet());
		this.tree.setSelectedItem(keys.get(this.index), true);
	}
	protected void displayOpts(ArrayList<Option> currOpt) {//three cols
		if (currOpt.size()>0){
			configsCellTable.setRowCount(currOpt.size(), true);					    
			configsCellTable.setRowData(0, currOpt);
			errorLabel_2.setText("Displaying Options.");//TODO TESTING
		}
		else errorLabel_2.setText("No Options to Display!");
	}
	
	//STATIC POPUP METHOD for saving new mediatypes
	private static class MyPopup extends PopupPanel {

	    public MyPopup() {
	      super(false);
	      //add stuff to the popup
	      	VerticalPanel verticalPanel = new VerticalPanel();
			verticalPanel.setSize("100%", "100%");
			
			TextBox testPatternTextBox = new TextBox();
			verticalPanel.add(testPatternTextBox);
			
			TextArea MediaIDPattsTextArea = new TextArea();
			verticalPanel.add(MediaIDPattsTextArea);
			
			TextBox sourceURLTextBox = new TextBox();
			verticalPanel.add(sourceURLTextBox);
			
			TextBox nameTextBox = new TextBox();
			verticalPanel.add(nameTextBox);
			
			ListBox comboBox_1 = new ListBox();
			verticalPanel.add(comboBox_1);
			
			Button okButton = new Button("OK");
			verticalPanel.add(okButton);
			//TODO ADD BUTTON TO CLOSE/DONE
	      setWidget(verticalPanel);
	    }
	  }
	
	/****************************************************************************************************************
	 * 
	 * Start of RPC calling methods
	 ****************************************************************************************************************/
	protected void reRequestFurtherPlaylist(PlaylistItem empty){
		if (comboBox.getSelectedIndex()<=0){
			this.errorLabel.setText("Nothing found, try increasing depth and re-requesting...");
			PlayMaker.this.rereqIndex = 0;
			return;
		} else PlayMaker.this.rereqIndex++;
		if (PlayMaker.this.rereqIndex > comboBox.getSelectedIndex()){
			this.errorLabel.setText("Request ended.");
			PlayMaker.this.rereqIndex = 0;
			return;
		}
		PlayMaker.this.getChanButton.setEnabled(false);
		this.errorLabel.setText("Entering level "+PlayMaker.this.rereqIndex+"...");
		this.webPlaylistService.resumeRecursion(empty, requestMediaOnly, this.requestID, new AsyncCallback<PlaylistItem>() {
			
			@Override
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
				PlayMaker.this.rereqIndex = 0;
				PlayMaker.this.getChanButton.setEnabled(true);
			}
			@Override
			public void onSuccess(PlaylistItem result) {
				PlayMaker.this.getChanButton.setEnabled(true);
				if (result!=null) {
					PlayMaker.this.errorLabel.setText("");
					if (result.hasChildren()){
						ArrayList<PlaylistItem> resultHolder = new ArrayList<PlaylistItem>();
						resultHolder.add(result);
						PlayMaker.this.addPlaylist(resultHolder, PlayMaker.this.tree, PlayMaker.this.errorLabel);
					}
				}
				//verify whether we need to keep requesting
				if (PlayMaker.this.comboBox.getSelectedIndex()>PlayMaker.this.rereqIndex) PlayMaker.this.reRequestFurtherPlaylist(result);
				else {
					PlayMaker.this.rereqIndex = 0;
					if (!playableItems.isEmpty()) 
						updateAllMediaNode();
					//Store this URL and all media found in it to build the media db
					PlayMaker.this.requestStoreMedias(new ArrayList<PlaylistItem>(playableItems.values()),result.getURL().toString());
				}
			}
		});
}
	protected void requestLoadGlobalPlsFromServer(){//TODO add UI for this specific funct
		this.webPlaylistService.requestLoadGlobalPls(new AsyncCallback<PlaylistItem>() {
			@Override
			public void onFailure(Throwable caught) {
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");				
			}
			@Override
			public void onSuccess(PlaylistItem result) {
				if (result!=null&&(result.hasChildren())) {//has to have children to be properly loaded
					PlayMaker.this.errorLabel.setText("");				
					ArrayList<PlaylistItem> resultHolder = new ArrayList<PlaylistItem>();
					resultHolder.add(result);
					if(!PlayMaker.this.addPlaylist(resultHolder, PlayMaker.this.tree, PlayMaker.this.errorLabel))
						PlayMaker.this.errorLabel.setText("Global Playlist Empty!");
					else {
						if (!playableItems.isEmpty()) {
							updateAllMediaNode();
						}
					}
				} else PlayMaker.this.errorLabel.setText("Global Playlist Empty!");				
			}			
		});
	}
	protected void requestLoadPlsFromServer() {
		// TODO ONLY SEND RPC TO SERVER IF THIS IS FIRST TIME USER IS LOGGED ON AND NOTHING IS ON THE PLAYLIST?
		//FIGURE OUT USE CASE SCENARIO - WHEN DO WE REQUEST TO LOAD SAVED PLAYLISTS? DOES THIS NEED A LOAD BUTTON?
		if (this.isLoggedIn){//Load the previous session if logged in
			this.webPlaylistService.requestLoadCurUserPls(new AsyncCallback<PlaylistItem>() {
				@Override
				public void onFailure(Throwable caught) {
					// Show the RPC error message to the user
					PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
				}
				@Override
				public void onSuccess(PlaylistItem result) {
					if ((result!=null)&&(result.hasChildren())) {//has to have children to be properly loaded
						PlayMaker.this.errorLabel.setText("");				
						ArrayList<PlaylistItem> resultHolder = new ArrayList<PlaylistItem>();
						resultHolder.add(result);
						if (!PlayMaker.this.addPlaylist(resultHolder, PlayMaker.this.tree, PlayMaker.this.errorLabel))
							PlayMaker.this.errorLabel.setText("No Media Loaded");
						else {
							if (!playableItems.isEmpty()) {
								updateAllMediaNode();
							}
						}
					} else PlayMaker.this.errorLabel.setText("No Media Loaded");				
				}
			});
			if (this.loginInfo.isAdmin())
				PlayMaker.this.requestLoadGlobalPlsFromServer(); //Load the global playlist if admin is viewing
		}else PlayMaker.this.errorLabel.setText("Not logged in - cannot load.");
	}
	/*
	 * Saves the passed playlist or all non-owned or pre-owned playlists if passed NULL.
	 */
	protected void requestSavePlsToServer(PlaylistItem savePlsit) {
		if (this.isLoggedIn){
			LinkedHashSet<PlaylistItem> toSave= new LinkedHashSet<PlaylistItem> ();			
			if (savePlsit==null){ // if not specified by caller save every requested URL
				for (TreeItem t : requestedURLS.values()){
					PlaylistItem svPls = PlayMaker.this.getPlaylistItemFromTreeItem(t);
					if ((svPls.getOwner()==null)||(svPls.getOwner()==loginInfo.getUniqueID()))
						toSave.add(svPls);
				}
			} else toSave.add(savePlsit);
			boolean notAuth = false;
			for (PlaylistItem item : toSave){
				if ((item.getOwner()!=null)&&(item.getOwner()!=loginInfo.getUniqueID())){
					toSave.remove(item);
					notAuth = true;
				}
			}
			if (!toSave.isEmpty()){ 
					this.webPlaylistService.requestSaveUserPls(toSave, loginInfo, new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
							// TODO Auto-generated method stub
							PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
						}
			
						@Override
						public void onSuccess(Boolean result) {
							if (result.booleanValue()) 
								PlayMaker.this.errorLabel.setText("Saved successfully.");
							else PlayMaker.this.errorLabel.setText("Unable to save... are you logged in?");				
						}
					});
			} else {
				if (notAuth)
					this.errorLabel.setText("Can't save this... owned by media-loop");
				else this.errorLabel.setText("Nothing to save!");
			}
		} else PlayMaker.this.errorLabel.setText("Not logged in - cannot save.");
	}
	
	private void requestMediatypesListFromServer()	throws IllegalArgumentException {
		this.webPlaylistService.requestAllPlayTypes(new AsyncCallback<ArrayList<MediaType>>(){

			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(ArrayList<MediaType> result) {		
				CheckBox checkBox;
				for (MediaType mt : result){ //first run through update the UI
					typesFilter.put(mt, true);//all types enabled by default
					checkBox = new CheckBox(mt.getName());
					checkBox.setValue(true);
					mediatypesVerticalPanel.add(checkBox);
				}
				PlayMaker.this.requestLoadMediaDB();
			}			
		});
		this.errorLabel_1.setText("Mediatypes Query in progress...");
	}
	private void requestLoadMediaDB(){
		ArrayList<String> request = new ArrayList<String>();
		for (MediaType mt : typesFilter.keySet()){
			if (typesFilter.get(mt)) request.add(mt.getName());
		}
		this.webPlaylistService.requestLoadPls(request, new AsyncCallback<ArrayList<PlaylistItem>>(){

			@Override
			public void onFailure(Throwable caught) {
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(ArrayList<PlaylistItem> result) {
				if (result!=null) {
					PlayMaker.this.errorLabel.setText("");
					if (!result.isEmpty()){
						for (PlaylistItem p : result) {
							if (!p.hasChildren())
								result.remove(p);
						}
						//final TreeItem playlist = PlayMaker.this.addPlaylist(result, PlayMaker.this.tree_1, PlayMaker.this.errorLabel_1);				
						if (!PlayMaker.this.addPlaylist(result, PlayMaker.this.tree_1, null))
							 PlayMaker.this.errorLabel_1.setText("Could not find any media to add for the selected types!");
						else PlayMaker.this.errorLabel_1.setText("");
					} else PlayMaker.this.errorLabel_1.setText("No media has been stored for the selected mediatypes");
				} else
					PlayMaker.this.errorLabel_1.setText("No Media at URL... or Connection Failed");				
			}
		});
		this.errorLabel_1.setText("Loading Media DB for selected types...");
	}
	private void requestPlaylistFromServer(String urlToServer)
	throws IllegalArgumentException {
		urlToServer = urlToServer.trim();
		this.errorLabel.setText("");
		this.getChanButton.setEnabled(false);
		if (!ClientFieldVerifier.isValidURL(urlToServer)) {
			// CHECK TO SEE IF HTTP: IS MISSING
			final String pref = "http://" + urlToServer;
			if (!ClientFieldVerifier.isValidURL(pref)) { // MyURL IS STILL BAD
				this.errorLabel.setText("Please enter a valid MyURL");
				// TODO never reaches this due to having prepended http://, add	another check besides isValidURL
				return;
			} else
				urlToServer = pref;
		}		
		int internalLevel;
		if (this.internalZeroRec) internalLevel= 0;
		else internalLevel = PlayMaker.internalReqDepthMax;
		this.requestID = Utility.generateRequestID();
		this.webPlaylistService.requestPlaylist(urlToServer, true, internalLevel, requestMediaOnly, this.requestID,
				new AsyncCallback<PlaylistItem>() {
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				PlayMaker.this.getChanButton.setEnabled(true);
				PlayMaker.this.rereqIndex = 0;
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
			}

			public void onSuccess(PlaylistItem result) {
				PlayMaker.this.getChanButton.setEnabled(true);
				if (result!=null) {
					PlayMaker.this.errorLabel.setText("");
					if (result.hasChildren()){
						ArrayList<PlaylistItem> resultHolder = new ArrayList<PlaylistItem>();
						resultHolder.add(result);
						boolean addedSomething = PlayMaker.this.addPlaylist(resultHolder, PlayMaker.this.tree, PlayMaker.this.errorLabel);
						//verify whether we need to keep requesting
						if (PlayMaker.this.comboBox.getSelectedIndex()>PlayMaker.this.rereqIndex) PlayMaker.this.reRequestFurtherPlaylist(result);
						else {//if this result had all empty children then rerequest anyways
							if (!addedSomething) PlayMaker.this.reRequestFurtherPlaylist(result);
							else {//found something and request is not deeper
								PlayMaker.this.rereqIndex = 0;
								if (!playableItems.isEmpty()) 
									updateAllMediaNode();
								//Store this URL and all media found in it to build the media db
								PlayMaker.this.requestStoreMedias(new ArrayList<PlaylistItem>(playableItems.values()),result.getURL().toString());
							}
						}
					} else PlayMaker.this.reRequestFurtherPlaylist(result);
				} else PlayMaker.this.errorLabel.setText("No Media at URL... or Connection Failed"); //TODO RETRY WITH 0 LEVELS IF =NULL?
			}
		});
		if (PlayMaker.this.requestMediaOnly)
			this.errorLabel.setText("Requesting Playlist...");
		else this.errorLabel.setText("Requesting Sitemap...");
	}
	//Asks the server to store these medias if not already stored... nothing happens on success ?
	private void requestStoreMedias(ArrayList <PlaylistItem> plsToStore, String name) throws IllegalArgumentException{
	this.webPlaylistService.storeIfNotAlready(plsToStore,name, new AsyncCallback<Boolean>(){
		@Override
		public void onFailure(Throwable caught) {
			PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
		}
		@Override
		public void onSuccess(Boolean result) {
			// TODO Auto-generated method stub
		}
	});
	}
	//TODO add DEFAULTS button to UI to use this method (the combobox tries to load first)
	private void requestDefaultPrefsFromServer(PlayerType type)
	throws IllegalArgumentException {
		this.webPlaylistService.requestDefaultConfigs(type, new AsyncCallback<PlayerConfig>(){

			@Override
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(PlayerConfig result) {
				mediaPlayersManager.initSettings(result);
				PlayMaker.this.displayOpts(result.getOptions());
			}		
		});
		errorLabel_2.setText("Requesting Default Preferences for "+type);
	}
	
	//TODO create save playerprefs button and add UI	
	//This method is called everytime changes the playertype in the MyPlayer config tab.
	//@returns the defaultprefs for a playertype if the user doesnt have any saved PlayerConfigs
	private void requestLoadPlayerPrefsFromServer(PlayerType type) 
	throws IllegalArgumentException{
		this.webPlaylistService.requestLoadUserConfigs(type, new AsyncCallback<PlayerConfig>(){

			@Override
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				PlayMaker.this.errorLabel.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(PlayerConfig result) {
				mediaPlayersManager.initSettings(result);
				PlayMaker.this.displayOpts(result.getOptions());
			}			
		});
		errorLabel_2.setText("Loading Preferences for "+type);
	}
}